/* ///////////////////////////////////////////////////////////////////////////// */
/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//        Copyright (c) 2005-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"

#if defined (UMC_ENABLE_H261_VIDEO_DECODER)

#include <string.h>
#include "umc_h261_video_decoder.h"
#include "umc_video_data.h"
#include "h261decoder.h"

namespace UMC
{

VideoDecoder *CreateH261Decoder() { return (new H261VideoDecoder()); }

Status H261VideoDecoder::AllocateBuffers()
{
  Status  status = UMC_OK;
  Ipp32s   w, h;
  Ipp8u   *p;

  w = m_decInfo->picture_width;
  h = m_decInfo->picture_height;
  status = m_pMemoryAllocator->Alloc(&m_decInfo->mid_cur, w * h + (w * h >> 1), UMC_ALLOC_PERSISTENT);
  if (status != UMC_OK)
    return status;
  p = (Ipp8u*)m_pMemoryAllocator->Lock(m_decInfo->mid_cur);
  ippsSet_8u(0, p, w * h);
  ippsSet_8u(128, p + w * h, w * h >> 1);
  status = m_pMemoryAllocator->Unlock(m_decInfo->mid_cur);
  if (status != UMC_OK)
    return status;

  status = m_pMemoryAllocator->Alloc(&m_decInfo->mid_ref, w * h + (w * h >> 1), UMC_ALLOC_PERSISTENT);
  if (status != UMC_OK)
    return status;
  p = (Ipp8u*)m_pMemoryAllocator->Lock(m_decInfo->mid_ref);
  ippsSet_8u(0, p, w * h);
  ippsSet_8u(128, p + w * h, w * h >> 1);
  status = m_pMemoryAllocator->Unlock(m_decInfo->mid_ref);
  if (status != UMC_OK)
    return status;

  return status;
}

Status H261VideoDecoder::FreeBuffers()
{
  Status  status = UMC_OK;

  if (m_decInfo->mid_cur) {
    status = m_pMemoryAllocator->Free(m_decInfo->mid_cur);
    if (status != UMC_OK)
      return status;
  }
  if (m_decInfo->mid_ref) {
    status = m_pMemoryAllocator->Free(m_decInfo->mid_ref);
    if (status != UMC_OK)
      return status;
  }
  return status;
}

void H261VideoDecoder::LockBuffers()
{
  Ipp32s w, h;
  w = m_decInfo->picture_width;
  h = m_decInfo->picture_height;

  m_decInfo->curPic[0] = (Ipp8u*)m_pMemoryAllocator->Lock(m_decInfo->mid_cur);
  m_decInfo->curPic[1] = m_decInfo->curPic[0] + w * h;
  m_decInfo->curPic[2] = m_decInfo->curPic[1] + (w * h >> 2);

  m_decInfo->refPic[0] = (Ipp8u*)m_pMemoryAllocator->Lock(m_decInfo->mid_ref);
  m_decInfo->refPic[1] = m_decInfo->refPic[0] + w * h;
  m_decInfo->refPic[2] = m_decInfo->refPic[1] + (w * h >> 2);
}

Status H261VideoDecoder::UnlockBuffers()
{
  Status status = UMC_OK;

  status = m_pMemoryAllocator->Unlock(m_decInfo->mid_cur);
  if (status != UMC_OK)
    return status;
  status = m_pMemoryAllocator->Unlock(m_decInfo->mid_ref);
  if (status != UMC_OK)
    return status;
  return status;
}

Status H261VideoDecoder::Init(BaseCodecParams *lpInit)
{
  Status  status = UMC_OK;
  VideoDecoderParams *pParam = DynamicCast<VideoDecoderParams> (lpInit);

  Close();
  if (NULL == pParam)
    return UMC_ERR_NULL_PTR;
  // create default memory allocator
  status = BaseCodec::Init(lpInit);
  if (status != UMC_OK)
    return status;
  m_dec_time_base = -1.0;
  m_Param = *pParam;
  m_IsInit = false;
  memset(m_decInfo, 0, sizeof(H261DEC_STATE));
  m_decInfo->tr = 255;
  if (NULL != pParam->m_pData) {
    if (pParam->m_pData->GetTime() >= 0.0)
      m_dec_time_base = pParam->m_pData->GetTime();
    m_decInfo->buflen = pParam->m_pData->GetDataSize();
    if (m_decInfo->buflen > 0) {
      m_decInfo->buffer = reinterpret_cast<Ipp8u *> (pParam->m_pData->GetDataPointer());
      status = InsideInit(&m_Param);
    }
  }
  if (UMC_OK == status) {
    m_PostProcessing = pParam->pPostProcessing;
    // set VOP size if it is unknown in splitter
    if (pParam->info.clip_info.width == 0 || pParam->info.clip_info.height == 0) {
      if (m_decInfo->picture_width == 0 && m_decInfo->picture_height == 0)
        return UMC_ERR_INIT;
      else {
        pParam->info.clip_info.width = m_Param.info.clip_info.width; // = m_decInfo->picture_width;
        pParam->info.clip_info.height = m_Param.info.clip_info.height; // = m_decInfo->picture_height;
      }
    }
  }
  m_ClipInfo = m_Param.info;
  m_IsInitBase = true;
  return status;
}


Status H261VideoDecoder::InsideInit(VideoDecoderParams *pParam)
{
  Status status = UMC_OK;
  Ipp32s ret;
  if ((ret = initDecoder(m_decInfo)) != 0) {
    status = UMC_ERR_SYNC;
    return status;
  }
  pParam->info.clip_info.width = m_decInfo->picture_width;
  pParam->info.clip_info.height = m_decInfo->picture_height;

  status = AllocateBuffers();
  if (status != UMC_OK) {
    return UMC_ERR_ALLOC;
  }

  m_IsInit = true;
  return status;
}


Status H261VideoDecoder::GetFrame(MediaData* in, MediaData* out)
{
  Status status = UMC_OK;
  Ipp64f pts;
  Ipp32s ret;

  out->SetInvalid(1);

  if (!m_IsInitBase)
    return UMC_ERR_NOT_INITIALIZED;
  if (out == NULL)
    return UMC_ERR_NULL_PTR;

  if (in) {
    m_decInfo->bufptr = m_decInfo->buffer = (Ipp8u*)in->GetDataPointer();
    m_decInfo->buflen = in->GetDataSize();
    // find bit-offset of start code (it can be not byte-aligned)
    for (m_decInfo->bitoff = 0; m_decInfo->bitoff < 9; m_decInfo->bitoff++) {
      Ipp32s sc = (m_decInfo->bufptr[0] << (m_decInfo->bitoff + 24)) +
                  (m_decInfo->bufptr[1] << (m_decInfo->bitoff + 16)) +
                  (m_decInfo->bufptr[2] << (m_decInfo->bitoff + 8)) +
                  (m_decInfo->bufptr[3] << (m_decInfo->bitoff));
      if ((sc & 0xfffff000) == 0x00010000)
        break;
    }
    if (m_decInfo->bitoff > 8)
      return UMC_ERR_SYNC;
    if (m_decInfo->bitoff == 8) {
      m_decInfo->bufptr++;
      m_decInfo->bitoff = 0;
    }
  }
  if (!m_IsInit) {
    if (in == NULL)
      return UMC_ERR_NOT_ENOUGH_DATA;
    m_Param.m_pData = in;
    status = InsideInit(&m_Param);
    if (status != UMC_OK) {
      in->MoveDataPointer(in->GetDataSize() - m_decInfo->buflen);
      return UMC_ERR_INIT;
    }
  }
  LockBuffers();

  out->SetInvalid(0);

  for (;;) {
    if (in == NULL) {
      UnlockBuffers();
      return UMC_ERR_NOT_ENOUGH_DATA;
    } else {
      if ((ret = getPictureHeader(m_decInfo)) != 0) {
        out->SetInvalid(1);
        status = UMC_ERR_SYNC;
        break;
      }
      if (getPicture(m_decInfo) != 0) {
        out->SetInvalid(1);
        status = UMC_OK;
      }
    }
    break;
  };
  pts = -1.0;
  if (in) {
    if (in->GetTime() >= 0.0 && m_dec_time_base < 0.0) {
      m_dec_time_base = in->GetTime();
      m_decInfo->time = 0;
    }
    pts = in->GetTime();
  }
  if (pts < 0) {
    pts = (Ipp64f)m_decInfo->time * (1001.0 / 30000.0);
    if (m_dec_time_base >= 0.0)
      pts += m_dec_time_base;
  }
  if (UMC_OK == status || UMC_WRN_INVALID_STREAM == status) {
    Status pp_status;
    if (m_LastDecodedFrame.GetColorFormat() != YUV420) {
      m_LastDecodedFrame.Init(m_decInfo->picture_width,
                              m_decInfo->picture_height,
                              YUV420);
    }
    m_LastDecodedFrame.SetFrameType(m_decInfo->picType == 0 ? I_PICTURE : P_PICTURE);
    m_LastDecodedFrame.SetTime(pts);
//    m_LastDecodedFrame.SetPlanePointer(m_decInfo->refPic[0], 0);
//    m_LastDecodedFrame.SetPlanePointer(m_decInfo->refPic[1], 1);
//    m_LastDecodedFrame.SetPlanePointer(m_decInfo->refPic[2], 2);
    m_LastDecodedFrame.SetPlanePointer(m_decInfo->curPic[0], 0);
    m_LastDecodedFrame.SetPlanePointer(m_decInfo->curPic[1], 1);
    m_LastDecodedFrame.SetPlanePointer(m_decInfo->curPic[2], 2);
    m_LastDecodedFrame.SetPlanePitch(m_decInfo->picture_width, 0);
    m_LastDecodedFrame.SetPlanePitch(m_decInfo->picture_width >> 1, 1);
    m_LastDecodedFrame.SetPlanePitch(m_decInfo->picture_width >> 1, 2);
    if (!m_PostProcessing) {
      m_PostProcessing = m_allocatedPostProcessing = createVideoProcessing();
    }
    pp_status = m_PostProcessing->GetFrame(&m_LastDecodedFrame, out);
    if (pp_status != UMC_OK)
      status = pp_status;
  }
  if (in) {
    size_t stDecidedData;
    if ((size_t)m_decInfo->bufptr - (size_t)m_decInfo->buffer < (size_t)m_decInfo->buflen)
      stDecidedData = m_decInfo->buflen - ((size_t)m_decInfo->bufptr - (size_t)m_decInfo->buffer);
    else
      stDecidedData = 0;
    in->MoveDataPointer(in->GetDataSize() - static_cast<Ipp32u>(stDecidedData));
    // can't calculate time for the next frame
    in->SetTime(-1.0);
  }
  UnlockBuffers();
  return status;
}


Status H261VideoDecoder::Close(void)
{
  m_IsInitBase = false;
  if (m_IsInit) {
    FreeBuffers();
    // close default memory allocator
    BaseCodec::Close();
    m_IsInit = false;
  }
  return UMC_OK;
}


H261VideoDecoder::H261VideoDecoder(void)
{
  m_IsInit = m_IsInitBase = false;
  m_decInfo = new H261DEC_STATE;
}


H261VideoDecoder::~H261VideoDecoder(void)
{
  Close();
  delete m_decInfo;
}

Status H261VideoDecoder::GetInfo(BaseCodecParams* info)
{
  VideoDecoderParams *pParams;
  if (!m_IsInitBase)
    return UMC_ERR_NOT_INITIALIZED;
  if (info == NULL)
    return UMC_ERR_NULL_PTR;
  pParams = DynamicCast<VideoDecoderParams> (info);
  if (NULL != pParams) {
    *pParams = m_Param;
  } else {
    if (!m_IsInit)
      return UMC_ERR_NOT_INITIALIZED;
    //    info->profile = m_Param.profile;
    //    info->level = m_Param.level;
  }
  return UMC_OK;
}


Status  H261VideoDecoder::ResetSkipCount()
{
  return UMC_ERR_NOT_IMPLEMENTED;
}


Status  H261VideoDecoder::SkipVideoFrame(Ipp32s /*count*/)
{
  return UMC_ERR_NOT_IMPLEMENTED;
}


Ipp32u H261VideoDecoder::GetNumOfSkippedFrames(void)
{
  return 0;
}


Status H261VideoDecoder::Reset(void)
{
  if (!m_IsInitBase)
    return UMC_ERR_NOT_INITIALIZED;
  if (m_IsInit) {
    m_dec_time_base = -1.0;
    m_decInfo->tr = 255;
    m_decInfo->time = 0;
  }
  return UMC_OK;
}


Status H261VideoDecoder::GetPerformance(Ipp64f * /*perf*/)
{
    return UMC_ERR_NOT_IMPLEMENTED;
}

} // end namespace UMC

#endif // defined (UMC_ENABLE_H261_VIDEO_DECODER)
